home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Cream of the Crop 3
/
Cream of the Crop 3.iso
/
comm
/
wnos5src.zip
/
POPSERV.C
< prev
next >
Wrap
Text File
|
1993-08-09
|
10KB
|
444 lines
/* POP Server state machine - see RFC 937
*
* also see other credits in popcli.c
* 10/89 Mike Stockett wa7dyx
* Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
* Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
* some code streaming - DB3FL.911111
*/
#include <stdio.h>
#include <time.h>
#include <sys/stat.h>
#if defined(__STDC__) || defined(__TURBOC__)
#include <stdarg.h>
#endif
#include <ctype.h>
#include <setjmp.h>
#include "global.h"
#include "config.h"
#ifdef POP
#include "mbuf.h"
#include "cmdparse.h"
#include "socket.h"
#include "proc.h"
#include "files.h"
#include "pop.h"
#include "server.h"
#define BITS_PER_WORD 16
#define isSOM(x) ((strncmp(x,"From ",5) == 0))
/* Command string specifications */
static char ackd_cmd[] = "ACKD",
acks_cmd[] = "ACKS",
#ifdef POP_FOLDERS
fold_cmd[] = "FOLD ",
#endif
login_cmd[] = "HELO",
nack_cmd[] = "NACK",
quit_cmd[] = "QUIT",
read_cmd[] = "READ",
retr_cmd[] = "RETR";
/* Response messages */
static char count_rsp[] = "#%d messages in this folder\n",
error_rsp[] = "- ERROR: %s\n",
greeting_msg[] = "+ POP2 %s\n",
/* length_rsp[] = "=%ld bytes in this message\n", */
length_rsp[] = "=%ld characters in msg #%d\n",
msg_line[] = "%s\n",
no_mail_rsp[] = "+ No mail, sorry\n",
no_more_rsp[] = "=%d No more messages in this folder\n",
signoff_msg[] = "+ Bye, thanks for calling\n";
static void near
state_error(struct popserv *scb,char *msg)
{
usprintf(scb->socket,error_rsp,msg);
scb->state = DONE;
}
static int near
newmail(struct popserv *scb)
{
struct stat folder_stat;
if(stat(scb->path,&folder_stat) != 0) {
state_error(scb,"Unable to get old mail folder's status");
return(FALSE);
} else {
return((folder_stat.st_size > scb->folder_file_size) ? TRUE : FALSE);
}
}
static int near
isdeleted(struct popserv *scb,int msg_no)
{
unsigned int offset = (--msg_no) / BITS_PER_WORD;
unsigned int mask = 1 << (msg_no % BITS_PER_WORD);
return (((scb->msg_status[offset]) & mask) ? TRUE : FALSE);
}
static void near
close_folder(struct popserv *scb)
{
char line[BUF_LEN];
FILE *fd;
int deleted = FALSE, msg_no = 0;
struct stat folder_stat;
if(scb->wf == NULLFILE)
return;
if(!scb->folder_modified) {
/* no need to re-write the folder if we have not modified it */
Fclose(scb->wf);
scb->wf = NULLFILE;
xfree(scb->msg_status);
scb->msg_status = NULL;
return;
}
if(newmail(scb)) {
/* copy new mail into the work file
* and save the message count for later
*/
if ((fd = Fopen(scb->path,READ_TEXT,0,1)) == NULLFILE)
return;
fseek(scb->wf,0,SEEK_END);
fseek(fd,scb->folder_file_size,SEEK_SET);
while(!feof(fd)) {
fgets(line,BUF_LEN,fd);
fputs(line,scb->wf);
}
Fclose(fd);
}
/* now create the updated mail folder */
if((fd = Fopen(scb->path,WRITE_TEXT,0,1)) == NULLFILE)
return;
rewind(scb->wf);
while(fgets(line,BUF_LEN,scb->wf) != NULL) {
if (isSOM(line)) {
msg_no++;
deleted = (msg_no <= scb->folder_len) ? isdeleted(scb,msg_no) : FALSE;
}
if(deleted)
continue;
fputs(line,fd);
}
Fclose(fd);
/* trash the updated mail folder if it is empty */
if((stat(scb->path,&folder_stat) == 0) && (folder_stat.st_size == 0))
unlink(scb->path);
Fclose(scb->wf);
scb->wf = NULLFILE;
xfree(scb->msg_status);
scb->msg_status = NULL;
}
static void near
do_cleanup(struct popserv *scb)
{
close_folder(scb);
scb->state = DONE;
}
static void near
deletemsg(struct popserv *scb,int msg_no)
{
if(scb->def == FALSE) {
unsigned int offset = (--msg_no) / BITS_PER_WORD;
unsigned int mask = 1 << (msg_no % BITS_PER_WORD);
scb->msg_status[offset] |= mask;
scb->folder_modified = TRUE;
}
}
static void near
print_message_length(struct popserv *scb)
{
char *cp;
if (scb->msg_len > 0 || scb->msg_num <= scb->folder_len) {
cp = length_rsp;
} else {
cp = no_more_rsp;
}
usprintf(scb->socket,cp,scb->msg_len,scb->msg_num);
}
static void near
get_message(struct popserv *scb,int msg_no)
{
scb->msg_len = 0;
if (msg_no > scb->folder_len) {
scb->curpos = 0;
scb->nextpos = 0;
return;
} else {
char line[BUF_LEN];
/* find the message and its length */
rewind(scb->wf);
while (!feof(scb->wf) && (msg_no > -1)) {
if (msg_no > 0)
scb->curpos = ftell(scb->wf);
fgets(line,BUF_LEN,scb->wf);
rip(line);
if (isSOM(line))
msg_no--;
if (msg_no != 0)
continue;
scb->nextpos = ftell(scb->wf);
scb->msg_len += (strlen(line)+2); /* Add CRLF */
}
}
if (scb->msg_len > 0)
fseek(scb->wf,scb->curpos,SEEK_SET);
/* we need the pointers even if the message was deleted */
if(isdeleted(scb,scb->msg_num))
scb->msg_len = 0;
}
static void near
read_message(struct popserv *scb)
{
if(scb->buf[sizeof(read_cmd) - 1] == ' ') {
scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
} else {
scb->msg_num++;
}
get_message(scb,scb->msg_num);
print_message_length(scb);
scb->state = ITEM;
}
static void near
retrieve_message(struct popserv *scb)
{
char line[BUF_LEN];
long cnt = scb->msg_len;
if(cnt == 0) {
state_error(scb,"Msg already deleted");
return;
}
while(!feof(scb->wf) && (cnt > 0)) {
fgets(line,BUF_LEN,scb->wf);
rip(line);
usprintf(scb->socket,msg_line,line);
cnt -= (strlen(line) + 2); /* Compensate for CRLF */
}
scb->state = NEXT;
}
static void near
open_folder(struct popserv *scb)
{
char line[BUF_LEN];
FILE *fd;
struct stat folder_stat;
scb->folder_len = 0;
scb->folder_file_size = 0;
if(stat(scb->path,&folder_stat)){
usputs(scb->socket,no_mail_rsp);
return;
}
scb->folder_file_size = folder_stat.st_size;
if((fd = Fopen(scb->path,READ_TEXT,0,1)) == NULLFILE)
return;
if((scb->wf = Tmpfile(0,1)) == NULLFILE) {
Fclose(fd);
return;
}
while(fgets(line,BUF_LEN,fd) != NULL) {
/* scan for begining of a message */
if (isSOM(line))
scb->folder_len++;
/* now put the line in the work file */
fputs(line,scb->wf);
}
Fclose(fd);
scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
if((((scb->folder_len) % BITS_PER_WORD) != 0) || (scb->msg_status_size == 0))
scb->msg_status_size++;
scb->msg_status = cxallocw(scb->msg_status_size,sizeof(unsigned int));
usprintf(scb->socket,count_rsp,scb->folder_len);
scb->state = MBOX;
}
#ifdef POP_FOLDERS
static void near
select_folder(struct popserv *scb)
{
char *cp = scb->buf;
while(*cp++ != ' ') ;
strcpy(scb->username,cp);
if (scb->wf != NULL)
close_folder(scb);
open_folder(scb);
}
#endif
void
popserv(int s,void *unused,void *p)
{
struct popserv *scb = mxallocw(sizeof(struct popserv));
sockowner(s,Curproc); /* We own it now */
sockmode(s,SOCK_ASCII);
scb->folder_modified = FALSE;
scb->socket = s;
scb->state = AUTH;
usprintf(scb->socket,greeting_msg,Hostname);
log(scb->socket,9983,"POP open");
for(;;) {
if (scb->state == DONE
|| recvline(s,scb->buf,BUF_LEN) == -1) {
/* He closed on us */
break;
}
rip(scb->buf);
if (*scb->buf == '\0') /* Ignore blank cmd lines */
continue;
switch(scb->state) {
case AUTH: {
char tmp[5], cp[MAXPATH];
sscanf(scb->buf,"%4s %s %s",tmp,scb->username,cp);
if(strcmp(tmp,login_cmd) == 0) {
if(!userlogin(IPPORT_POP,(void *)scb,cp)) {
#ifdef ENHLOG
log(scb->socket,IPPORT_POP,"POP access DENIED to %s",scb->username);
#endif
state_error(scb,"Access denied");
do_cleanup(scb);
break;
}
#ifdef ENHLOG
log(scb->socket,IPPORT_POP,"POP access granted to %s",scb->username);
#endif
open_folder(scb);
} else if(strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
do_cleanup(scb);
} else
state_error(scb,"(AUTH) Expected HELO or QUIT command");
break;
}
case MBOX:
if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
read_message(scb);
#ifdef POP_FOLDERS
else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
select_folder(scb);
#endif
else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
do_cleanup(scb);
} else
state_error(scb,
#ifdef POP_FOLDERS
"(MBOX) Expected FOLD, READ, or QUIT command");
#else
"(MBOX) Expected READ or QUIT command");
#endif
break;
case ITEM:
if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
read_message(scb);
#ifdef POP_FOLDERS
else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
select_folder(scb);
#endif
else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
retrieve_message(scb);
else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
do_cleanup(scb);
else
state_error(scb,
#ifdef POP_FOLDERS
"(ITEM) Expected FOLD, READ, RETR, or QUIT command");
#else
"(ITEM) Expected READ, RETR, or QUIT command");
#endif
break;
case NEXT:
if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
/* ACKD processing */
deletemsg(scb,scb->msg_num);
scb->msg_num++;
get_message(scb,scb->msg_num);
} else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
/* ACKS processing */
scb->msg_num++;
get_message(scb,scb->msg_num);
} else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
/* NACK processing */
fseek(scb->wf,scb->curpos,SEEK_SET);
} else {
state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
break;
}
print_message_length(scb);
scb->state = ITEM;
break;
case DONE:
break;
default:
state_error(scb,"(TOP) State Error!!");
break;
}
}
if(scb->state == DONE)
usputs(scb->socket,signoff_msg);
do_cleanup(scb);
log(scb->socket,9983,"POP close");
close_s(scb->socket);
if(scb->wf != NULLFILE)
Fclose(scb->wf);
if(scb->msg_status != NULL)
xfree(scb->msg_status);
if(scb->path != NULLCHAR)
xfree(scb->path);
xfree(scb);
}
#endif /* POP */